package queue

import (
	"fmt"
	"gin/service"
	"gin/transcoder/ffmpeg"
	"gin/validate"
	"github.com/disintegration/imaging"
	"github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	pngquant "github.com/yusukebe/go-pngquant"
	"image/png"
	"log"
	"os"
	"strconv"
	"strings"
)

var (
	videoFormat       = "mp4"
	fileSaveOverwrite = true
)

// 初始化 ffmpeg
func initFFMPEGConfig() (ffmpegConf ffmpeg.Config, opts ffmpeg.Options) {

	if ffmpegConf.FfmpegBinPath == "" {
		// ffmpeg 配置
		ffmpegConf = ffmpeg.Config{
			FfmpegBinPath:   viper.GetString("ffmpeg.ffmpegBinPath"),
			FfprobeBinPath:  viper.GetString("ffmpeg.ffprobeBinPath"),
			ProgressEnabled: true,
		}

		opts = ffmpeg.Options{
			OutputFormat: &videoFormat,
			Overwrite:    &fileSaveOverwrite,
		}
	}

	return
}

// 执行转码
func handleTranscode(Param validate.TranscodeVideoParam) (err error) {

	// 获取 ffmepg 配置
	ffmpegConf, opts := initFFMPEGConfig()

	switch Param.Type {
	case "audio", "file", "video":
		// 音频和文件直接同步到 COS
		err = service.SynchronizeToCos(Param.Folder, Param.Folder+"/"+Param.Md5Name+"."+Param.Ext, Param.PathPrefix)
		break
	case "images":
		err = imageTranscode(Param)
		break
	case "video_transcode":
		err = videoTranscode(&ffmpegConf, opts, Param)
		break
	default:
		// 不支持类型直接ack忽略
		return
	}

	if err != nil {
		logrus.WithFields(logrus.Fields{
			"route": fmt.Sprintf("trancoder type: %s", Param.Type),
			"request_param": logrus.Fields{
				"Param": Param,
			},
		}).Error(err.Error())
	} else {
		// 更新数据
		err = service.UpdateStatusByMd5Name(Param.Md5Name, "yes")
	}

	return
}

// 媒体转码
func videoTranscode(ffmpegConf *ffmpeg.Config, opts ffmpeg.Options, Param validate.TranscodeVideoParam) (err error) {
	// 获取视频中间帧缩略图
	midTimeFloat, _ := strconv.ParseFloat(Param.Duration, 64)
	midTimeString := fmt.Sprintf("%.2f", midTimeFloat/2)

	err = ffmpeg.New(ffmpegConf).
		Input(Param.TempPath).
		Output("./"+Param.Path+Param.Md5Name+"_0.jpg").
		GenerateThumbnail(midTimeString, Param.Width, Param.Height, false)

	// 同步COS记录
	validate.SynchronizeToCosNameString += Param.Folder + "/" + Param.Md5Name + "_0.jpg" + ","

	if err != nil {
		return
	}

	// 执行转码,写入队列
	progress, err := ffmpeg.
		New(ffmpegConf).
		Input(Param.TempPath).
		Output("./" + Param.Path + Param.Md5Name + ".mp4").
		WithOptions(opts).
		Start(opts)

	// 同步COS记录
	validate.SynchronizeToCosNameString += Param.Folder + "/" + Param.Md5Name + ".mp4" + ","

	if err != nil {
		return
	}

	for msg := range progress {
		log.Printf("%+v", msg)
	}

	// 更新ext
	_, _ = service.UpdateParamByMd5Name(Param.Md5Name, videoFormat)
	// 没有错误同步到COS
	err = service.SynchronizeToCos(Param.Folder, validate.SynchronizeToCosNameString, Param.PathPrefix)

	// 处理完删除临时文件
	err = os.Remove(Param.TempPath)

	return
}

// 压缩图片
func compressImage(imageMetadata validate.TranscodeVideoParam) (pngTempOutputPath string, err error) {

	// 图片文件大于500kb则需要被压缩，否则跳过
	if imageMetadata.UseCompress == "yes" && imageMetadata.FileSize >= 512000 && imageMetadata.Ext == "png" {

		// 压缩png图片
		pngFile, pngErr := os.Open(imageMetadata.TempPath)

		if pngErr != nil {
			err = pngErr
			return
		}

		// decode png into image.Image
		imgPng, pngErr := png.Decode(pngFile)
		if pngErr != nil {
			err = pngErr
			return
		}

		imgPngCompress, pngErr := pngquant.Compress(imgPng, "10")
		if pngErr != nil {
			err = pngErr
			return
		}

		pngErr = pngFile.Close()
		if pngErr != nil {
			err = pngErr
			return
		}

		pngTempPath := viper.GetString("server.staticPath") + "temp/"
		pngTempOutputPath = pngTempPath + "/" + imageMetadata.Md5Name + "_compress." + imageMetadata.Ext

		out, pngErr := os.Create(pngTempOutputPath)
		if pngErr != nil {
			err = pngErr
			return
		}

		defer out.Close()

		pngErr = png.Encode(out, imgPngCompress)

		if pngErr != nil {
			err = pngErr
			return
		}
	} else {
		pngTempOutputPath = imageMetadata.TempPath
	}

	return
}

func imageTranscode(imageMetadata validate.TranscodeVideoParam) (err error) {
	if imageMetadata.Ext == "gif" {
		// gif 格式不处理直接拷贝，同步COS记录
		validate.SynchronizeToCosNameString += imageMetadata.Folder + "/" + imageMetadata.Md5Name + "_origin." + imageMetadata.Ext + ","

		err = service.SynchronizeToCos(imageMetadata.Folder, validate.SynchronizeToCosNameString, imageMetadata.PathPrefix)

		if err != nil {
			return
		}
	} else {

		openTempPath, compressErr := compressImage(imageMetadata)

		if compressErr != nil {
			err = compressErr
			return
		}

		// 压缩尺寸
		EncodeOption := imaging.PNGCompressionLevel(png.BestCompression)
		EncodeOption = imaging.JPEGQuality(80)

		// 加载图片
		img, OpenErr := imaging.Open(openTempPath)

		if OpenErr != nil {
			err = OpenErr
			return
		}

		// 保存原图
		err = imaging.Save(img, "./"+imageMetadata.Path+imageMetadata.Md5Name+"_origin."+imageMetadata.Ext, EncodeOption)

		// 同步COS记录
		validate.SynchronizeToCosNameString += imageMetadata.Folder + "/" + imageMetadata.Md5Name + "_origin." + imageMetadata.Ext + ","

		// 处理更多尺寸
		if imageMetadata.ImageSize != "" {
			imageSizeArray := strings.Split(imageMetadata.ImageSize, ",")
			var tempW, tempH int
			for index := range imageSizeArray {
				sizeArray := strings.Split(imageSizeArray[index], "x")
				tempW, _ = strconv.Atoi(sizeArray[0])
				tempH, _ = strconv.Atoi(sizeArray[1])
				dstImage := imaging.Fill(img, tempW, tempH, imaging.Center, imaging.Lanczos)
				err = imaging.Save(dstImage, "./"+imageMetadata.Path+imageMetadata.Md5Name+"_"+imageSizeArray[index]+"."+imageMetadata.Ext, EncodeOption)
				if err != nil {
					return
				}

				// 同步COS记录
				validate.SynchronizeToCosNameString += imageMetadata.Folder + "/" + imageMetadata.Md5Name + "_" + imageSizeArray[index] + "." + imageMetadata.Ext + ","
			}
		}

		// 没有错误同步到COS
		if err == nil {
			err = service.SynchronizeToCos(imageMetadata.Folder, validate.SynchronizeToCosNameString, imageMetadata.PathPrefix)
		}

		// 处理完删除临时文件
		if openTempPath == imageMetadata.TempPath {
			err = os.Remove(imageMetadata.TempPath)
		} else {
			err = os.Remove(imageMetadata.TempPath)
			err = os.Remove(openTempPath)
		}

		if err != nil {
			return
		}
	}

	return
}
